using System;
using System.Collections.Generic;

namespace Grammars.Core
{
    public abstract class Rule<T> : GrammarItem<T>
        where T : Node, new()
    {
        private readonly List<Action<ParameterList<T>>> semanticRules;
        private readonly List<Func<ParameterList<T>,bool>> semanticPredicates;

        protected Rule()
        {
            semanticRules = new List<Action<ParameterList<T>>>();
            semanticPredicates = new List<Func<ParameterList<T>, bool>>();
        }

        protected internal void AddRule(Action<ParameterList<T>> rule)
        {
            if (rule == null)
                throw new ArgumentNullException("rule");

            semanticRules.Add(rule);
        }

        protected internal void AddRule(Action<T> rule)
        {
            AddRule(p => rule(p[0]));
        }

        protected internal void AddRule(Action rule)
        {
            AddRule((ParameterList<T> p) => rule());
        }

        protected internal void AddPredicate(Func<ParameterList<T>, bool> predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate");

            semanticPredicates.Add(predicate);
        }

        protected internal void AddPredicate(Func<T, bool> predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate");

            semanticPredicates.Add(p => predicate(p[0]));
        }

        protected internal void AddPredicate(Func<bool> predicate)
        {
            AddPredicate((ParameterList<T> x) => predicate());
        }

        public IEnumerable<Action<ParameterList<T>>> SemanticRules
        {
            get { return semanticRules; }
        }

        protected void ApplyRules(T[] item)
        {
            foreach (var attributeRule in semanticRules)
                attributeRule(new ParameterList<T>(item));
        }

        protected bool CheckPredicates(T[] items)
        {
            foreach (var predicate in semanticPredicates)
                if (!predicate(new ParameterList<T>(items)))
                    return false;

            return true;
        }

        protected bool CheckPredicates(T items)
        {
            foreach (var predicate in semanticPredicates)
                if (!predicate(new ParameterList<T>(items)))
                    return false;

            return true;
        }

        protected void ApplyRules(T item)
        {
            foreach (var attributeRule in semanticRules)
                attributeRule(new ParameterList<T>(item));
        }
    }
}